package com.jweb.framework.ws.wsdl;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.wsdl.Definition;
import javax.wsdl.Import;
import javax.wsdl.Types;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.schema.Schema;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLLocator;
import javax.wsdl.xml.WSDLReader;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.util.Assert;
import org.springframework.ws.wsdl.WsdlDefinitionException;
import org.springframework.ws.wsdl.wsdl11.Wsdl11Definition;
import org.springframework.xml.transform.ResourceSource;
import org.springframework.xml.xsd.XsdSchemaCollection;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

import com.jweb.framework.core.util.XMLUtils;
import com.jweb.framework.ws.channel.ServiceDefinition;
import com.jweb.framework.ws.channel.ServiceManager;
import com.jweb.framework.ws.soap.addressing.client.WSAddressingHead;
import com.jweb.framework.ws.support.CommonsXsdSchemaCollectionJWeb;

/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：WSDL文件解析类
 */
public class WsdlWebServiceDefinition implements ServiceDefinition , BeanNameAware , InitializingBean , ApplicationContextAware , Wsdl11Definition {
	
	private String beanName ;
	private Resource wsdl ;
	private XsdSchemaCollection xsdSchemaCollection ;
	private String location ;
	private Definition definition ;
	private boolean useAddressing = false ;
	private WSAddressingHead wsAddressingHead ;
	private ApplicationContext applicationContext ;
	
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext ;
	}
	
	public WSAddressingHead getWsAddressingHead(){
		return this.wsAddressingHead ;
	}
	
	public void setWsAddressingHead(WSAddressingHead wsAddressingHead){
		this.wsAddressingHead = wsAddressingHead ;
	}
	
	public boolean isUseAddressing(){
		return this.useAddressing ;
	}
	
	public void setUseAddressing(boolean useAddressing){
		this.useAddressing = useAddressing ;
	}
	
	public String getBeanName(){
		return this.beanName ;
	}
	
	public void setBeanName(String beanName){
		this.beanName = beanName ;
	}
	
	public Definition getDefinition() {
		
		return this.definition ;
	}

	public XsdSchemaCollection getSchemaCollection() {

		return this.xsdSchemaCollection ; 
	}
	
	public void setWsdl(Resource resource){
		this.wsdl = resource ;
	}
	
	public void setLocation(String s){
		this.location = s ;
	}
	
	public String getLocation(){
		return this.location ;
	}
	
	static Resource getResource(WsdlWebServiceDefinition wsdlWebServiceDefinition){
		return wsdlWebServiceDefinition.wsdl ;
	}
	
	/**
	 * 解析WSDL文件
	 */
	public void afterPropertiesSet() throws Exception {
		
		Assert.notNull(this.wsdl , "wsdl is required");
		Assert.notNull(this.wsdl , "wsdl '" + this.wsdl + "' is required");
		
		try {
			
			WSDLFactory wsdlFactory = WSDLFactory.newInstance();
			WSDLReader wsdlReader = wsdlFactory.newWSDLReader();
			wsdlReader.setFeature("javax.wsdl.verbose", false);
			this.definition = wsdlReader.readWSDL(new ParserWSDLLocator(this));
			if(this.definition.getServices().size() != 1){
				throw new IllegalStateException("required only 1 service in WSDL");
			}
			
		} catch (WSDLException e) {
			throw new WsdlDefinitionException(e.getMessage() , e);
		}
		
		ArrayList arrayList = new ArrayList();
		parseDefinition(this.definition , arrayList);
		
		Resource resource[] = (Resource[])arrayList.toArray(new Resource[0]);
		CommonsXsdSchemaCollectionJWeb commonsXsdSchemaCollectionJKurrent = new CommonsXsdSchemaCollectionJWeb(resource);
		commonsXsdSchemaCollectionJKurrent.setResourceLoader(new DefaultResourceLoader());
		commonsXsdSchemaCollectionJKurrent.setInline(true);
		commonsXsdSchemaCollectionJKurrent.afterPropertiesSet();
		this.xsdSchemaCollection = commonsXsdSchemaCollectionJKurrent ;
		
		ServiceManager serviceManager = this.applicationContext.getBean(ServiceManager.class);
		serviceManager.injectServiceDefinition(this);
		
	}
	
	private void parseDefinition(Definition definition , List list) throws TransformerException {
		Map map = definition.getImports();
		for(Iterator it = map.values().iterator() ; it.hasNext() ;){
			List list1 = (List)it.next();
			for(int i = 0 ; i<list1.size() ;i++){
				Import import1 = (Import) list1.get(i);
				parseDefinition(import1.getDefinition() , list);
			}
		}
		
		Types types = definition.getTypes();
		if(types == null){
			return ;
		}
		
		List list2 = types.getExtensibilityElements();
		for(int j = 0 ; j < list2.size() ; j++){
			Element element = ((Schema)list2.get(j)).getElement();
			final InputStream inputStream = XMLUtils.toInputStream(new DOMSource(element));
			list.add(new InputStreamResource(inputStream){
					public String getDescription() {
			          return "Inline schema for " + WsdlWebServiceDefinition.getResource(WsdlWebServiceDefinition.this);
			        }

			        public URL getURL() throws IOException {
			          return WsdlWebServiceDefinition.getResource(WsdlWebServiceDefinition.this).getURL(); //="/localhost/weather/WEB-INF/wsdl/server/AntiCounterfeitRsltRecvSrv.wsdl"
			        }

			        public InputStream getInputStream() throws IOException
			        {
			          if (inputStream.markSupported()) {
			            inputStream.reset();
			            return inputStream ;
			          }
			          return super.getInputStream();
			        }
			});
		}
		
	}
	
	public Source getSource(){
		
		try {
			XMLReader xmlReader = XMLReaderFactory.createXMLReader();
			return new ResourceSource(xmlReader , this.wsdl);
		} catch (SAXException e) {
			throw new WsdlDefinitionException("Could not create XMLReader" ,e);
		} catch (IOException e){
			throw new WsdlDefinitionException((new StringBuilder("Could not create source from ")).append(this.wsdl).toString() , e);
		}
		
	}
	
	static class ParserWSDLLocator implements WSDLLocator {
		
		WsdlWebServiceDefinition wsdlWebServiceDefinition ;
		Resource resource ;
		String uriStr ;
		
		ParserWSDLLocator(WsdlWebServiceDefinition wsdlWebServiceDefinition){
			this.wsdlWebServiceDefinition = wsdlWebServiceDefinition ;
			this.resource = WsdlWebServiceDefinition.getResource(wsdlWebServiceDefinition);
		}
		
		public void close(){
			
		}
		
		public InputSource getBaseInputSource(){
			try {
				
				InputStream is = XMLUtils.toInputStream(wsdlWebServiceDefinition.getSource());
				return new InputSource(is);
			} catch (TransformerException e) {
				IllegalArgumentException iae = new IllegalArgumentException();
				iae.initCause(e);
				throw iae ;
			}
		}
		
		public String getBaseURI(){
			try {
				return this.resource.getURI().toString();
			} catch (IOException e) {
				IllegalArgumentException iae = new IllegalArgumentException();
				iae.initCause(e);
				throw iae ;
			}
		}
		
		public InputSource getImportInputSource(String s , String s1){
			
			try {
				UrlResource urlResource = new UrlResource(s);
				String s2 = this.resource.getURI().toString().substring(0 , this.resource.getURI().toString().lastIndexOf("/"));
				
				URI uri = new URI(s2);
				if(uri.isOpaque()){
					uri = new URI(uri.getSchemeSpecificPart());
				}
				URI uri1 = urlResource.createRelative(s1).getURI();
				if(uri1.isOpaque()){
					uri1 = new URI(uri1.getSchemeSpecificPart());
				}
				
				uriStr = uri.relativize(uri1).toString();
				return new InputSource(urlResource.createRelative(s1).getInputStream());
				
			} catch (Exception e) {
				IllegalArgumentException iae = new IllegalArgumentException();
				iae.initCause(e);
				throw iae ;
			}
		}

		public String getLatestImportURI() {
			
			try {
				return this.resource.createRelative(this.uriStr).getURI().toString();
				
			} catch (IOException e) {
				IllegalArgumentException iae = new IllegalArgumentException();
				iae.initCause(e);
				throw iae ;
			}
			
		}
		
		
		
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
}
